package com.chrilwe.pigpig.filesystem.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import com.alibaba.fastjson.JSON;
import com.chrilwe.pigpig.filesystem.common.FileUploadStatus;
import com.chrilwe.pigpig.filesystem.common.ResultEnum;
import com.chrilwe.pigpig.filesystem.common.ex.BadMergeFileException;
import com.chrilwe.pigpig.filesystem.common.request.PreUploadRequest;
import com.chrilwe.pigpig.filesystem.common.response.PreUploadResult;
import com.chrilwe.pigpig.filesystem.common.response.UploadChunkResult;
import com.chrilwe.pigpig.filesystem.mapper.BigfileMapper;
import com.chrilwe.pigpig.filesystem.mapper.ChunkfileMapper;
import com.sun.tools.javac.util.List;

import cn.chrilwe.pigpig.core.model.filesystem.BigFile;
import cn.chrilwe.pigpig.core.model.filesystem.ChunkFile;
import cn.chrilwe.pigpig.core.util.TimeFormaterUtil;

/**
  * 文件上传
 * @author chrilwe
 * @date 2020-10-6
 */
@Service
public class FileUploadService {
	
	@Value("${video.tmp.path}")
	private String tmp;
	
	private static final int chunkSize = 10 * 1024 * 1024;//块文件大小10m
	
	@Autowired
	private BigfileMapper bigfileMapper;
	
	@Autowired
	private ChunkfileMapper chunkfileMapper;
	
	@Autowired
	private HttpServletRequest request;
	
	public PreUploadResult preUpload(PreUploadRequest request) throws IOException {
		String fileMd5 = request.getFileMd5();
		String fileOriginName = request.getFileOriginName();
		long fileSize = request.getFileSize();
		String ext = request.getExt();
		BigFile bigFile = getBigFile(fileMd5, fileOriginName, fileSize, ext);
		if(bigFile.getSchedule() == fileSize) {
			if(bigFile.getStatus() == FileUploadStatus.UPLOADING) {
				BigFile bf = new BigFile();
				bf.setStatus(FileUploadStatus.UPLOADED);
				bigfileMapper.updateBigFile(bigFile);
				
				mergeChunkFile(fileMd5);
			}
			return new PreUploadResult(ResultEnum.FILE_UPLOAD_FINISHED.getCode(),ResultEnum.FILE_UPLOAD_FINISHED.getName(),bigFile.getSchedule(), chunkSize);
		}
		return new PreUploadResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName(), bigFile.getSchedule(), chunkSize);
	}
	
	protected BigFile getBigFile(String fileMd5, String filename, long fileSize, String ext) {
		String tmpPath = getTmpPath(fileMd5);
		int userId = 0;
		BigFile bigfile = bigfileMapper.selectBigFileByMd5UserId(fileMd5, userId);
		if(bigfile == null) {
			bigfile = new BigFile();
			bigfile.setFileMD5(fileMd5);
			bigfile.setFileName(UUID.randomUUID().toString() + "." + ext);
			bigfile.setOriginFileName(filename + "." + ext);
			bigfile.setFileSize(fileSize);
			bigfile.setSchedule(0);
			bigfile.setTmpPath(tmpPath);
			bigfile.setChunks(fileSize%chunkSize>0?(fileSize/chunkSize+1):fileSize/chunkSize);
			bigfile.setUserId(userId);
			bigfile.setSchedule(0);
			bigfile.setStatus(FileUploadStatus.UPLOADING);
			bigfileMapper.insertBigFile(bigfile);
		} 
		return bigfile;
	}
	
	protected String getTmpPath(String fileMd5) {
		String path = tmp + "/" + fileMd5;
		File file = new File(path);
		if(!file.exists()) {
			file.mkdirs();
		}
		return path;
	}
	
	public UploadChunkResult uploadChunk(MultipartFile file, String fileMd5, String chunkName) throws FileNotFoundException, IOException {
		int userId = 1;
		BigFile bigfile = bigfileMapper.selectBigFileByMd5UserId(fileMd5, userId);
		if(bigfile == null) {
			return new UploadChunkResult(ResultEnum.PLEASE_PREUPLOAD_FIRST.getCode(), ResultEnum.PLEASE_PREUPLOAD_FIRST.getName());
		}
		long size = file.getSize();
		if(size != chunkSize) {
			return new UploadChunkResult(ResultEnum.ERROR_CHUNK_SIZE.getCode(), ResultEnum.ERROR_CHUNK_SIZE.getName());
		}
		String tmpPath = bigfile.getTmpPath();
		File f = new File(tmpPath + "/" + chunkName);
		IOUtils.copy(file.getInputStream(), new FileOutputStream(f));
		
		ChunkFile chunkFile = new ChunkFile();
		chunkFile.setChunkSize(chunkSize);
		chunkFile.setCreateTime(TimeFormaterUtil.format("yyyy-MM-dd HH:mm:ss", new Date()));
		chunkFile.setFileId(bigfile.getFileId());
		chunkFile.setTmpPath(tmpPath);
		chunkFile.setName(chunkName);
		chunkFile.setStatus(1);
		chunkfileMapper.insertChunkFile(chunkFile);
		return new UploadChunkResult(ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getName());
	}
	
	protected void mergeChunkFile(String fileMd5) throws IOException {
		BigFile bf = new BigFile();
		bf.setStatus(FileUploadStatus.MERGING);
		bigfileMapper.updateBigFile(bf);
		int userId = 1;
		BigFile bigfile = bigfileMapper.selectBigFileByMd5UserId(fileMd5, userId);
		String tmpPath = bigfile.getTmpPath();
		File file = new File(tmpPath);
		String[] list = file.list();
		if(list != null) {
			Integer[] target = new Integer[list.clone().length];
			for(int i=0; i<list.length; i++) {
				target[i] = Integer.parseInt(list[i]);
			}
			quickSort(target, 0, target.length-1);
			doMerge(target, tmpPath, bigfile.getOriginFileName());
			boolean v = validateFileMd5(tmpPath + "/" + bigfile.getOriginFileName(), bigfile.getFileMD5());
			if(!v) {
				bf.setStatus(FileUploadStatus.ERRFILE);
				bigfileMapper.updateBigFile(bf);
				throw new BadMergeFileException();
			}
			bf.setStatus(FileUploadStatus.MERGED);
			bigfileMapper.updateBigFile(bf);
			
			transformToM3u8(tmpPath + "/" + bigfile.getOriginFileName());
		}
	}
	
	protected void doMerge(Integer[] target, String tmpPath, String filename) {
		File mergeFile = new File(tmpPath + "/" + filename);
		FileOutputStream output = null;
		try {
			output = new FileOutputStream(mergeFile, true);
			for (Integer i : target) {
				FileInputStream input = null;
				try {
					File chunkFile = new File(tmpPath + "/" + i);
					input = new FileInputStream(chunkFile);
					IOUtils.copy(input, output);
					
					deleteChunkFile(tmpPath, i);
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					if(input != null) {
						input.close();
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if(output != null) {
				try {
					output.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	protected void deleteChunkFile(String tmpPath, int chunkName) {
		File file = new File(tmpPath + "/" + chunkName);
		file.delete();
	}
	
	protected void quickSort(Integer[] target, int left, int right) {
		if(left < right) {
			int privot = target[left];
			int begin = left;
			int end = right;
			
			while(begin < end) {
				while(begin < end && privot < target[end]) {
					end--;
				}
				target[begin] = target[end];
				while(begin < end && privot > target[begin]) {
					begin++;
				}
				target[end] = target[begin];
			}
			target[begin] = privot;
			quickSort(target, 0, begin-1);
			quickSort(target, begin+1, right);
		} else {
			return;
		}
	}
	
	//校验合并后的文件的md5是否与初始的文件一样
	protected boolean validateFileMd5(String path, String originFileMd5) {
		FileInputStream inputStream = null;
		try {
			File file = new File(path);
			inputStream = new FileInputStream(file);
			String fileMd5 = DigestUtils.md5DigestAsHex(inputStream);
			if(originFileMd5.equals(fileMd5)) {
				return true;
			}
		} catch (Exception e) {
			
		} finally {
			if(inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					
				}
			}
		}
		return false;
	}
	
	protected void transformToM3u8(String filePath) {
		System.out.println("将文件"+ filePath + "转化为m3u8");
	}
}
